const path = require('path');
const os = require('os');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const WebpackBar = require('webpackbar');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const FriendlyErrorsWebpackPlugin = require('@soda/friendly-errors-webpack-plugin');

const modifyVars = require('./webpack.modifyVars');
const globalVars = require('./webpack.globalVars');

const isDevelopment = process.env.NODE_ENV === 'development';
const cwd = process.cwd();
const threads = os.cpus().length;
const gzip = false; // 开启压缩

// loaders
const postCssLoader = {
  loader: 'postcss-loader',
  options: {
    postcssOptions: {
      plugins: ['postcss-preset-env', 'postcss-normalize', 'postcss-flexbugs-fixes'],
    },
  },
};
const cssLoaders = [
  isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader,
  {
    loader: 'css-loader',
    options: {
      modules: {
        auto: true,
        localIdentName: '[local]--[hash:base64:10]',
        exportLocalsConvention: 'camelCaseOnly',
      },
    },
  },
  postCssLoader,
];

// plugins
const webpackBar = new WebpackBar();
const eslintWebpackPlugin = new EslintWebpackPlugin({
  threads,
  context: path.resolve(cwd, 'src'),
});
const copyWebpackPlugin = new CopyWebpackPlugin({
  patterns: [{ from: 'public', noErrorOnMissing: true }],
});
const htmlWebpackPlugin = new HtmlWebpackPlugin({
  title: 'wind-portfolio',
  template: 'index.html',
  favicon: 'favicon.ico',
});
const definePlugin = new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
});
const miniCssExtractPlugin = new MiniCssExtractPlugin({
  filename: 'assets/css/[name].[contenthash].css',
  chunkFilename: 'assets/css/[name].[contenthash].css',
});
const cssMinimizerPlugin = new CssMinimizerPlugin();
const compressionPlugin = new CompressionPlugin();
const reactRefreshWebpackPlugin = new ReactRefreshWebpackPlugin({ overlay: false });
const friendlyErrorsWebpackPlugin = new FriendlyErrorsWebpackPlugin();

module.exports = {
  mode: process.env.NODE_ENV,
  entry: './src/index.tsx',
  output: {
    clean: true,
    filename: 'assets/js/[name].[contenthash].js',
    chunkFilename: 'assets/js/[name].[contenthash].js',
    path: path.resolve(cwd, 'dist'),
  },
  resolve: {
    alias: {
      '@': path.resolve(cwd, 'src'),
    },
    extensions: ['.jsx', '.js', '.ts', '.tsx'],
  },
  devtool: isDevelopment ? 'cheap-module-source-map' : undefined,
  devServer: {
    port: '3001',
    open: true,
    historyApiFallback: true,
    proxy: {
      '/api': 'http://localhost:8000',
    },
    client: {
      overlay: {
        errors: true,
        runtimeErrors: false,
        warnings: false,
      },
    },
  },
  optimization: {
    chunkIds: 'deterministic',
    runtimeChunk: true,
    minimize: !isDevelopment,
    minimizer: !isDevelopment ? [`...`, cssMinimizerPlugin] : undefined,
    splitChunks: {
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'initial',
          priority: -10,
          reuseExistingChunk: true,
        },
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  module: {
    rules: [
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
        exclude: /node_modules/,
        generator: {
          filename: 'assets/fonts/[name].[hash:6][ext]',
        },
      },
      {
        test: /\.(png|jpg|jpeg|gif|webp)$/i,
        type: 'asset',
        exclude: /node_modules/,
        parser: {
          dataUrlCondition: {
            maxSize: 10 * 1024,
          },
        },
        generator: {
          filename: 'assets/images/[name].[hash:6][ext]',
        },
      },
      {
        test: /\.svg$/i,
        exclude: /node_modules/,
        oneOf: [
          {
            resourceQuery: /url/,
            type: 'asset',
            parser: {
              dataUrlCondition: {
                maxSize: 10 * 1024,
              },
            },
            generator: {
              filename: 'assets/images/[name].[hash:6][ext]',
            },
          },
          {
            loader: '@svgr/webpack',
            options: {
              icon: true,
            },
          },
        ],
      },
      {
        test: /\.css$/i,
        oneOf: [
          {
            test: /\.css$/i,
            exclude: /\.module\.css$/i,
            use: cssLoaders,
          },
          { test: /\.module\.css$/i, use: cssLoaders },
        ],
      },
      {
        test: /\.less$/i,
        oneOf: [
          {
            test: /\.less$/i,
            exclude: /\.module\.less$/i,
            use: [
              ...cssLoaders,
              {
                loader: 'less-loader',
                options: {
                  lessOptions: { modifyVars, globalVars },
                },
              },
            ],
          },
          {
            test: /\.module\.less$/i,
            use: [
              ...cssLoaders,
              {
                loader: 'less-loader',
                options: {
                  lessOptions: { modifyVars, globalVars },
                },
              },
            ],
          },
        ],
      },
      {
        test: /\.[tj]sx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: {
              works: threads,
            },
          },
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              presets: [
                ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
                '@babel/preset-react',
                '@babel/preset-typescript',
              ],
              plugins: [
                isDevelopment && 'react-refresh/babel',
                '@babel/plugin-transform-runtime',
              ].filter(Boolean), // babel对一些公告方法使用了非常小的辅助代码，默认情况会被添加到所有使用它的文件中，使用这个插件可以将辅助代码从这里引用
            },
          },
        ],
      },
    ],
  },
  plugins: [
    definePlugin,
    htmlWebpackPlugin,
    copyWebpackPlugin,
    !isDevelopment && miniCssExtractPlugin,
    gzip && compressionPlugin,
    webpackBar,
    eslintWebpackPlugin,
    isDevelopment && reactRefreshWebpackPlugin,
    friendlyErrorsWebpackPlugin,
  ].filter(Boolean),
};
